from threading import Timer
import random
import multineuro.vector2d

class SpriteModel(object):
    """ Represents an object with an associated sprite 
        It has a initial postition.
    """

    def __init__(self, sprite, initial_pos=None):
        self.sprite = sprite
        self.timer = None
        self.initial_pos = initial_pos or sprite.rect.topleft
        self.moving = False
        self.hide()

    def move_to_beggining(self):
        next_hop = self.get_next_hop()
        if next_hop == (0, 0):
            #Moved to initial position
            self.sprite.rect.topleft = self.initial_pos
            self.sprite.start_to_listen()
            self.show_sprite()
            self.moving = False
            return

        if not self.moving:
            #Start to move.
            self.sprite.unsuscribe_tip()
            self.sprite.stop_listening()
            self.moving = True

        self.sprite.rect = self.sprite.rect.move(*next_hop);
        self.show_sprite()
        self.timer = Timer(0.005, lambda: self.move_to_beggining())
        self.timer.start()


    def get_next_hop(self):
        actual = multineuro.vector2d.v2d(self.sprite.rect.topleft)
        destiny = multineuro.vector2d.v2d(self.initial_pos)
        actual_direction = destiny - actual
        if actual_direction.ipos != (0, 0):
            fuerza = min(2000, actual_direction.length) / 2000
            actual_direction.angle = (actual_direction.angle - 100 * fuerza)
            actual_direction.length = (actual_direction.length / 1000) + 1
        return actual_direction.ipos

    def show_sprite(self):
        self.sprite.show()


    def hide(self):
        self.sprite.hide()

    def move(self, pos):
        self.sprite.rect.topleft = pos

class Subject(SpriteModel):

    """ Represents a Subject in the experiment, for example a monkey, a rabbit, 
    etc.
    """

    def __init__(self, sprite, sound):
        super(Subject, self).__init__(sprite)
        self.sound = sound
        self.channel = None
        self.sneezing_timer = None

    def action(self, objects):
        if (not self.is_doing() and objects.activates()):
            self.channel = self.sound.play(maxtime=1000)
            self.sprite.swap()
            self.sneezing_timer = Timer(1, self._finnish_action)
            self.sneezing_timer.start()

    def is_sneezing(self):
        return (self.sneezing_timer and self.sneezing_timer.is_alive())

    def is_doing(self):
        return self.moving or self.is_sneezing()

    def _finnish_action(self):
        self.sprite.swap()
        self.move_to_beggining()

class Tester(SpriteModel):
    """ Represents the testing device, a sensor. """
    def __init__(self, sprite, subject, stimuli):
        super(Tester, self).__init__(sprite)
        self.subject = subject
        self.stimuli = stimuli

    def test(self, sprite_model):
        sprite = sprite_model.sprite
        if sprite.rect.colliderect(self.sprite.rect):
            self.timer = Timer(2, lambda : self.check_for_dock(sprite))
            self.timer.start()

    def check_for_dock(self, sprite):
        if sprite.rect.colliderect(self.sprite.rect):
            self.trigger()

    def trigger(self):
        self.subject.action(self.stimuli)

class Stimulus(SpriteModel):
    """ Represents the estimuli which the subject is tested against """
    def __init__(self, sprite):
        super(Stimulus, self).__init__(sprite)


class Stimuli():
    def __init__(self, stimuli_sprite, trial_configuration):
        self.configuration = trial_configuration
        self.sprites = stimuli_sprite
        self.stimuli = {}
        for estim_sprite in self.sprites:
            self.stimuli[estim_sprite] = Stimulus(self.sprites[estim_sprite])
        self._filter_available()

    def hide(self):
        for stimulus in self.stimuli:
            self.stimuli[stimulus].hide()

    def _filter_available(self):
        self.available = {}
        for t in self.configuration :
            names = t.names
            for name in names:
                if not self.available.has_key(name):
                    self.available[name] = self.stimuli[name]

